﻿#include "ssShader.h"
#include "file/ssReadFile.h"


namespace StarSeeker
{

ssShader::ssShader(const char* name)
:ssObject(name)
{
	m_vertexHandle = 0;
	m_programHandle = 0;
	m_piexlHandle = 0;
}

ssShader::~ssShader()
{
	Unload();
}

bool ssShader::CompileVertexShaderFromFile( const char* path )
{
	ssReadFile rf;
	rf.SetPath(path);
	if (!rf.OpenFile(false) || !rf.ReadAll())
	{
		SS_CONSOLE->Printf("[%s] load VertexShader file error!\n", Name());
		return false;
	}
	return CompileVertexShader(rf.GetBuffer().Buf(), rf.GetBuffer().Size());
}

void ssShader::Unload()
{
	if (m_programHandle)
	{
		glDetachShader(m_programHandle, m_vertexHandle);
		glDetachShader(m_programHandle, m_piexlHandle);
		m_programHandle = 0;
	}
	if (m_vertexHandle)
	{
		glDeleteShader(m_vertexHandle);
		m_vertexHandle = 0;
	}
	if (m_piexlHandle)
	{
		glDeleteShader(m_piexlHandle);
		m_piexlHandle = 0;
	}
}

bool ssShader::CompileVertexShader( const char* str, u32 strLen )
{
	m_vertexHandle = glCreateShader(GL_VERTEX_SHADER);
	if (m_vertexHandle==0)
	{
		SS_CONSOLE->Printf("[%s] glCreateShader error!\n", Name());
		return false;
	}
	SetShaderSource(m_vertexHandle, str, strLen);
	if (!CompileShader(m_vertexHandle))
	{
		return false;
	}
	if (!CreateShaderProgram())
	{
		return false;
	}
	m_vertexText = str;
	glAttachShader(m_programHandle, m_vertexHandle);
	return true;
}

bool ssShader::LinkShader()
{
	if (m_programHandle)
	{
		glLinkProgram(m_programHandle);
		GLint OK = 0;
		glGetProgramiv(m_programHandle, GL_LINK_STATUS, &OK);
		if (!OK)
		{
			GLint len = 0;
			glGetProgramiv(m_programHandle, GL_INFO_LOG_LENGTH, &len);
			if (len>0)
			{
				char* str = new char[len+1];
				glGetProgramInfoLog(m_programHandle, len, 0, str);
				SS_CONSOLE->Printf("[%s] glLinkProgram error:\n", Name());
				SS_CONSOLE->Print(str);
				delete [] str;
			}
			else
			{
				SS_CONSOLE->Printf("[%s] glLinkProgram error: Reason can not be obtained!\n", Name());
			}
			return false;
		}
		else
		{
			return true;
		}
	}
	return false;
}

void ssShader::Use()
{
	if (m_programHandle)
	{
		glUseProgram(m_programHandle);
		CheckGlError("glUseProgram");
	}
}

void ssShader::NonuseShader()
{
	glUseProgram(0);
}

bool ssShader::CompilePiexlShaderFromFile( const char* path )
{
	ssReadFile rf;
	rf.SetPath(path);
	if (!rf.OpenFile(false) || !rf.ReadAll())
	{
		SS_CONSOLE->Printf("[%s] load PiexlShader from file error!\n", Name());
		return false;
	}
	return CompilePiexlShader(rf.GetBuffer().Buf(), rf.GetBuffer().Size());
}

bool ssShader::CompilePiexlShader( const char* str, u32 strLen )
{
	m_piexlHandle = glCreateShader(GL_FRAGMENT_SHADER);
	if (m_piexlHandle==0)
	{
		SS_CONSOLE->Printf("[%s] glCreateShader error!\n", Name());
		return false;
	}
	SetShaderSource(m_piexlHandle, str, strLen);
	if (!CompileShader(m_piexlHandle))
	{
		return false;
	}
	if (!CreateShaderProgram())
	{
		return false;
	}
	m_piexlText = str;
	glAttachShader(m_programHandle, m_piexlHandle);
	return true;
}

bool ssShader::CompileShader( GLuint h )
{
	GLint OK = 0;
	GLint len = 0;
	glCompileShader(h);
	glGetShaderiv(h, GL_COMPILE_STATUS, &OK);
	if (!OK)
	{
		glGetShaderiv(h, GL_INFO_LOG_LENGTH, &len);
		if (len>0)
		{
			char* str = new char[len];
			glGetShaderInfoLog(h, len, 0, str);
			SS_CONSOLE->Printf("[%s] glCompileShader error:\n", Name());
			SS_CONSOLE->Print(str);
			delete [] str;
		}
		else
		{
			SS_CONSOLE->Printf("[%s] glCompileShader error: Reason can not be obtained!\n", Name());
		}
		return false;
	}
	return true;
}

bool ssShader::CreateShaderProgram()
{
	if (m_programHandle==0)
	{
		m_programHandle = glCreateProgram();
		if (m_programHandle==0)
		{
			SS_CONSOLE->Printf("[%s] glCreateProgram error!\n", Name());
			return false;
		}
	}
	return true;
}

void ssShader::SetShaderSource(GLuint h, const char* str, u32 strLen)
{
#ifdef SS_USE_GLES2
	const char* def = "#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n";
	const char* strArr[2] = { def, str };
	i32 lenArr[2] = { strlen(def), (i32)strLen };
	glShaderSource(h, 2, strArr, lenArr);
#else
	const char* strArr[1] = { str };
	i32 lenArr[1] = { (i32)strLen };
	glShaderSource(h, 1, strArr, lenArr);
#endif
}

void ssShader::SetUniformFloat( i32 id, const ssfloat* pf, u32 count )
{
#ifdef SS_USE_DOUBLE_FLOAT
	switch(count)
	{
	case 1:
		glUniform1d(id, *pf);
		break;
	case 2:
		glUniform2d(id, pf[0], pf[1]);
		break;
	case 3:
		glUniform3d(id, pf[0], pf[1], pf[2]);
		break;
	case 4:
		glUniform4d(id, pf[0], pf[1], pf[2], pf[3]);
		break;
	default:
		return;
	}
#else
	switch(count)
	{
	case 1:
		glUniform1f(id, *pf);
		break;
	case 2:
		glUniform2f(id, pf[0], pf[1]);
		break;
	case 3:
		glUniform3f(id, pf[0], pf[1], pf[2]);
		break;
	case 4:
		glUniform4f(id, pf[0], pf[1], pf[2], pf[3]);
		break;
	default:
		return;
	}
	CheckGlError("SetUniformVariables");
#endif
}

void ssShader::SetUniformInt(i32 id, const i32 * pi, u32 count)
{
	switch (count)
	{
	case 1:
		glUniform1i(id, *pi);
		break;
	case 2:
		glUniform2i(id, pi[0], pi[1]);
		break;
	case 3:
		glUniform3i(id, pi[0], pi[1], pi[2]);
		break;
	case 4:
		glUniform4i(id, pi[0], pi[1], pi[2], pi[3]);
		break;
	default:
		return;
	}
	CheckGlError("SetUniformVariables");
}

void ssShader::SetUniformVector(i32 id, const ssfloat* pf, u32 count, u32 vcount)
{
#ifdef SS_USE_DOUBLE_FLOAT
	switch(vcount)
	{
	case 1:
		glUniform1dv(id, count, pf);
		break;
	case 2:
		glUniform2dv(id, count, pf);
		break;
	case 3:
		glUniform3dv(id, count, pf);
		break;
	case 4:
		glUniform4dv(id, count, pf);
		break;
	default:
		return;
	}
#else
	switch(vcount)
	{
	case 1:
		glUniform1fv(id, count, pf);
		break;
	case 2:
		glUniform2fv(id, count, pf);
		break;
	case 3:
		glUniform3fv(id, count, pf);
		break;
	case 4:
		glUniform4fv(id, count, pf);
		break;
	default:
		return;
	}
#endif
	CheckGlError("SetUniformVector");
}

void ssShader::SetUniformMatrix(i32 id, const ssfloat* pf, u32 count, u32 mcount)
{
#ifdef SS_USE_DOUBLE_FLOAT
	switch(mcount)
	{
	case 2:
		glUniformMatrix2dv(id, count, GL_FALSE, pf);
		break;
	case 3:
		glUniformMatrix3dv(id, count, GL_FALSE, pf);
		break;
	case 4:
		glUniformMatrix4dv(id, count, GL_FALSE, pf);
		break;
	default:
		return;
	}
#else
	switch(mcount)
	{
	case 2:
		glUniformMatrix2fv(id, count, GL_FALSE, pf);
		break;
	case 3:
		glUniformMatrix3fv(id, count, GL_FALSE, pf);
		break;
	case 4:
		glUniformMatrix4fv(id, count, GL_FALSE, pf);
		break;
	default:
		return;
	}
#endif
	CheckGlError("SetUniformMatrix");
}

void ssShader::SetUniformTextureLayer(i32 id, u8 layer)
{
	glUniform1i(id, layer);
	CheckGlError("SetUniformTextureLayer");
}

GLint ssShader::GetUniformLocation( const char* name )
{
	return glGetUniformLocation(m_programHandle, name);
}

GLint ssShader::GetAttribLocation(const char* name)
{
	return glGetAttribLocation(m_programHandle, name);
}

//void ssShader::SetTextureUniformVariables( u8 layer, const char* name )
//{
//	GLint id = glGetUniformLocation(m_programHandle, name);
//	glUniform1i(id, layer);
//	CheckGlError("SetTextureUniformVariables");
//}

}
